from typing import Optional
from datetime import timedelta
from fastapi import HTTPException
from utils.abstract_repository import AbstractRepository
from utils.models.user import User
from utils.auth import pwd_context, verify_jwt_token
from utils.funcs import get_current_date

WORK_TIME_SLEEP = timedelta(minutes=30)


class UserRepository(AbstractRepository[User]):
    model = User

    async def insert(self, email: str, password: str) -> int:
        return await self.insert_one_with_data(
                email=email, password=pwd_context.hash(password))

    async def select_by_email(self, email: str) -> Optional[User]:
        return await self.select_one_where(self.model.email == email)

    async def select_by_residence(self, residence: int) -> list[User]:
        return await self.select_many_where(self.model.residence == residence)

    async def edit_name(self, user_id: int, name: str) -> None:
        await self.edit_by_id(user_id, name=name)
        return

    async def add_cash(self, user_id: int, amount: int) -> None:
        await self.edit_by_id(user_id, cash=self.model.cash+amount)
        return

    async def remove_cash(self, user_id: int, amount: int) -> None:
        await self.edit_by_id(user_id, cash=self.model.cash-amount)
        return

    async def set_sleep_work(self, user_id: int) -> None:
        await self.edit_by_id(user_id,
                              work_end=get_current_date() + WORK_TIME_SLEEP)
        return

    async def check_auth_and_get(self, token: str) -> User:
        decoded_data = verify_jwt_token(token)

        if not decoded_data:
            raise HTTPException(status_code=401, detail="Неправильный токен")

        user = await self.select_by_id(decoded_data["id"])

        if not user:
            raise HTTPException(status_code=401, detail="Непонятный юзернейм")

        return user

    async def give_cash(self, user_give: int,
                        user_take: int, amount: int) -> None:
        await self.remove_cash(user_give, amount)
        await self.add_cash(user_take, amount)
        return
